#ifndef __LINUX_OF_H__
#define __LINUX_OF_H__

#include "linux/init.h"
#include "linux/compiler.h"
#include "linux/bitops.h"   
#include "linux/string.h"
#include "linux/types.h"
#include "linux/list.h"
#include "linux/ioport.h"
#include "linux/device.h"
#include "linux/byteorder/generic.h"
#include "linux/of_platform.h"

struct of_dev_auxdata;
struct alias_prop;
struct of_phandle_iterator;
struct of_phandle_args;
struct device_node;
struct property;

extern void *of_get_property(struct device_node *np, char *name, uint32_t *lenp);
extern struct device_node *of_find_node_by_path( char *path);
extern struct device_node *of_get_parent( struct device_node *node);
extern struct property *of_find_property(struct device_node *np, char *name, uint32_t *lenp);
extern int of_property_read_string_helper( struct device_node *np,
                                           char *propname,  char **out_strs,
                                          size_t sz, int skip);
int of_property_read_variable_u32_array( struct device_node *np,
                                        char *propname, uint32_t *out_values,
                                        size_t sz_min, size_t sz_max);
int of_n_addr_cells(struct device_node *np);
int of_n_size_cells(struct device_node *np);
void of_alias_scan(void *(*dt_alloc)(uint64_t size, uint64_t align));
struct device_node *of_get_next_child( struct device_node *node, struct device_node *prev);
struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev);
int of_device_is_compatible( struct device_node *device, char *compat);
struct property *__of_find_property( struct device_node *np, char *name, uint32_t *lenp);
char *of_prop_next_string(struct property *prop, char *cur);
int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs);
int of_device_is_available( struct device_node *device);
int of_irq_count(struct device_node *dev);
int of_platform_default_populate(struct device_node *root, struct of_dev_auxdata *lookup, struct device *parent);
void of_dump_device_tree(void);
int of_property_match_string(struct device_node *np, char *propname, char *string);
struct device_node *of_find_matching_node_and_match(struct device_node *from, struct of_device_id *matches, struct of_device_id **match);
struct device_node *of_find_node_by_phandle(uint32_t handle);
int of_parse_phandle_with_args( struct device_node *np, char *list_name, char *cells_name, int index, struct of_phandle_args *out_args);

extern struct device_node *of_root;
extern struct device_node *of_chosen;
extern struct device_node *of_aliases;
extern struct device_node *of_stdout;

#define OF_DYNAMIC 1       /* node and properties were allocated via kzalloc */
#define OF_DETACHED 2      /* node has been detached from the device tree */
#define OF_POPULATED 3     /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */

#define OF_BAD_ADDR ((uint64_t)-1)

// 内核运行时的属性表示 ：内核将 DTB 解析后，将属性转换为此结构体，用于动态管理设备树
// 支持内核设备模型 ：与 struct device_node 结合，实现设备与驱动的绑定
struct property
{
    char *name;
    char *value;
    int length;
    struct property *next;
};

struct device_node
{
    char *name;
    char *type;
    char *full_name;
    uint32_t phandle;
    struct device_node *parent;
    struct device_node *child;
    struct device_node *sibling;
    struct property *properties;
    struct device_node *next; // 用于链表遍历

    unsigned long _flags;
    void *data;
};

#define MAX_PHANDLE_ARGS 16
struct of_phandle_args
{
    struct device_node *np;
    int args_count;
    uint32_t args[MAX_PHANDLE_ARGS];
};

struct of_phandle_iterator
{
    /* Common iterator information */
     char *cells_name;
    int cell_count;
     struct device_node *parent;

    /* List size information */
     uint32_t *list_end;
     uint32_t *phandle_end;

    /* Current position state */
     uint32_t *cur;
    uint32_t cur_count;
    uint32_t phandle;
    struct device_node *node;
};

struct alias_prop
{
    struct list_head link;
     char *alias;
    struct device_node *np;
    int id;
    char stem[0];
};

#define of_compat_cmp(s1, s2, l) strncasecmp((s1), (s2), l)
#define of_prop_cmp(s1, s2) strcmp((s1), (s2))
#define of_node_cmp(s1, s2) strcasecmp((s1), (s2))

#define __for_each_child_of_node(parent, child)                    \
    for (child = __of_get_next_child(parent, NULL); child != NULL; \
         child = __of_get_next_child(parent, child))

#define for_each_property_of_node(dn, pp) \
    for (pp = dn->properties; pp != NULL; pp = pp->next)

#define for_each_of_allnodes_from(from, dn) \
    for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))

#define for_each_child_of_node(parent, child)                    \
    for (child = of_get_next_child(parent, NULL); child != NULL; \
         child = of_get_next_child(parent, child))

#define of_for_each_phandle(it, err, np, ln, cn, cc)             \
    for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)), \
         err = of_phandle_iterator_next(it);                     \
         err == 0;                                               \
         err = of_phandle_iterator_next(it))

#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)

/*
 * OF address retrieval & translation
 */

/* Helper to read a big number; size is in cells (not bytes) */
static inline uint64_t of_read_number(uint32_t *cell, int size)
{
    uint64_t r = 0;

    while (size--)
    {
        r = (r << 32) | be32_to_cpu(*(cell++));
    }
    
    return r;
}

static inline int of_property_read_string_index( struct device_node *np,
                                                 char *propname,
                                                int index, char **output)
{
    int rc = of_property_read_string_helper(np, propname, output, 1, index);
    return rc < 0 ? rc : 0;
}

static inline struct device_node *of_node_get(struct device_node *node)
{
    return node;
}

static inline void of_node_put(struct device_node *node) {}

static inline int of_node_is_root( struct device_node *node)
{
    return node && (node->parent == NULL);
}

static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
{
    return test_bit(flag, (long *)(&n->_flags));
}

static inline int of_node_test_and_set_flag(struct device_node *n, unsigned long flag)
{
    return test_and_set_bit(flag, (unsigned char *)(&n->_flags));
}

static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
{
    set_bit(flag, (long *)(&n->_flags));
}

static inline void of_node_clear_flag(struct device_node *n, unsigned long flag)
{
    clear_bit(flag, (long *)&n->_flags);
}

static inline int of_property_read_u32_array( struct device_node *np,
                                              char *propname,
                                             uint32_t *out_values, size_t sz)
{
    int ret = of_property_read_variable_u32_array(np, propname, out_values,
                                                  sz, 0);
    if (ret >= 0)
        return 0;
    else
        return ret;
}

static inline int of_property_read_u32( struct device_node *np,
                                        char *propname,
                                       uint32_t *out_value)
{
    return of_property_read_u32_array(np, propname, out_value, 1);
}

static inline struct device_node *of_find_matching_node(struct device_node *from, struct of_device_id *matches)
{
    return of_find_matching_node_and_match(from, matches, NULL);
}

#define of_property_for_each_u32(np, propname, prop, p, u)	\
	for (prop = of_find_property(np, propname, NULL),	\
		p = of_prop_next_u32(prop, NULL, &u);		\
		p;						\
		p = of_prop_next_u32(prop, p, &u))

#define of_property_for_each_string(np, propname, prop, s)	\
	for (prop = of_find_property(np, propname, NULL),	\
		s = of_prop_next_string(prop, NULL);		\
		s;						\
		s = of_prop_next_string(prop, s))

#define for_each_node_by_name(dn, name) \
	for (dn = of_find_node_by_name(NULL, name); dn; \
	     dn = of_find_node_by_name(dn, name))
#define for_each_node_by_type(dn, type) \
	for (dn = of_find_node_by_type(NULL, type); dn; \
	     dn = of_find_node_by_type(dn, type))
#define for_each_compatible_node(dn, type, compatible) \
	for (dn = of_find_compatible_node(NULL, type, compatible); dn; \
	     dn = of_find_compatible_node(dn, type, compatible))
#define for_each_matching_node(dn, matches) \
	for (dn = of_find_matching_node(NULL, matches); dn; \
	     dn = of_find_matching_node(dn, matches))
#define for_each_matching_node_and_match(dn, matches, match) \
	for (dn = of_find_matching_node_and_match(NULL, matches, match); \
	     dn; dn = of_find_matching_node_and_match(dn, matches, match))

#define for_each_child_of_node(parent, child) \
	for (child = of_get_next_child(parent, NULL); child != NULL; \
	     child = of_get_next_child(parent, child))
#define for_each_available_child_of_node(parent, child) \
	for (child = of_get_next_available_child(parent, NULL); child != NULL; \
	     child = of_get_next_available_child(parent, child))

#define for_each_node_with_property(dn, prop_name) \
	for (dn = of_find_node_with_property(NULL, prop_name); dn; \
	     dn = of_find_node_with_property(dn, prop_name))

typedef int (*of_init_fn_2)(struct device_node *, struct device_node *);
typedef int (*of_init_fn_1_ret)(struct device_node *);
typedef void (*of_init_fn_1)(struct device_node *);


#define _OF_DECLARE(table, name, compat, fn, fn_type)			\
	static struct of_device_id __of_table_##name		\
		__used __section(__##table##_of_table)			\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn  }

#define OF_DECLARE_1(table, name, compat, fn) \
		_OF_DECLARE(table, name, compat, fn, of_init_fn_1)
#define OF_DECLARE_1_RET(table, name, compat, fn) \
		_OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret)
#define OF_DECLARE_2(table, name, compat, fn) \
		_OF_DECLARE(table, name, compat, fn, of_init_fn_2)

#endif
